Week 7 of 16

Flask Parts 5–7 + Claude Integration Planning

More Flask patterns, then sketch out the two features you'll build this week: AI generation and search

Day 31 75 minutes Watch

Day 31 of 80

What You're Preparing For

By End of Week 7

The Prompt Vault web app will do something genuinely useful: you type a shot description in the browser, click "Generate with Claude", and a production-ready AI video prompt appears on the page — written by Claude, ready to save to your JSON file. You'll also add a search bar so you can find any prompt by keyword.

Today is the preparation session: more Flask video content to fill in your mental model, then a planning exercise to design both features before you write a single line.

Today's Videos

Corey Schafer — Flask Parts 5, 6, and 7

Package structure, user authentication patterns, template inheritance (~75 min total) — type along

What to Skip vs. What to Keep

Parts 5–7 cover user accounts, password hashing, and SQLAlchemy models — none of which you'll use in the Prompt Vault. But don't skip them entirely. The template inheritance pattern ({% extends "base.html" %} and {% block content %}), error handlers (@app.errorhandler(404)), and url_for() are directly applicable to your project. Watch for those and type along with those sections.

What to Pay Attention To

Concept Why It Matters for Your Project
url_for("function_name") Generates URLs from function names — better than hardcoding "/add" everywhere. If you rename a route, url_for() updates automatically.
Template inheritance {% extends "base.html" %} lets you define a shared layout once. When you add more pages (a detail view, a generate page), they all inherit the same header and nav.
Error handlers @app.errorhandler(404) — catch bad URLs gracefully. A 404 on /delete/999 when there are only 5 prompts should show a friendly message, not a crash.
Flash messages flash("Prompt added!") — a built-in way to show one-time status messages after a redirect. Useful for "Saved!" and "Deleted!" confirmations.

Planning Feature 1: Generate with Claude

The /generate Route

A new form lets you type a shot description. When you submit it, Python calls Claude with a system prompt, receives the generated text, and passes it back to the same template. The generated prompt appears on the page with a "Save This" button. Clicking Save adds it to prompts.json.

Here's the skeleton — you'll fill this in on Day 32:

app.py — /generate route skeleton Python
@app.route("/generate", methods=["POST"])
def generate():
    shot = request.form.get("shot", "").strip()
    platform = request.form.get("platform", "Kling")

    if not shot:
        return redirect("/")

    # TODO: Call Claude here
    # message = claude.messages.create(...)
    # generated = message.content[0].text

    # TODO: Pass generated back to template
    prompts = load_prompts()
    return render_template("index.html",
        prompts=prompts,
        generated="...",
        gen_shot=shot,
        gen_platform=platform
    )
Why pass gen_shot and gen_platform back? Because when the user wants to save the generated prompt, the Save form needs to POST all three fields: platform, shot, and the generated prompt text. You can't save just the text — the full record needs all three.

.strip() — Remove leading/trailing whitespace from user input before using it. A shot description that's just spaces would produce a useless prompt.

Planning Feature 2: Keyword Search

The /search Route

A search bar at the top of the page. When the user types "aerial" and hits Enter, the page shows only prompts that contain "aerial" in the platform name, shot description, or prompt text. It's a GET request — the query goes in the URL: /search?q=aerial. That means it's bookmarkable and the back button works.

app.py — /search route sketch Python
@app.route("/search")
def search():
    # request.args reads GET parameters (from the URL ?q=...)
    query = request.args.get("q", "").lower()
    prompts = load_prompts()

    if query:
        results = [p for p in prompts
                   if query in p["platform"].lower()
                   or query in p["shot"].lower()
                   or query in p["prompt"].lower()]
    else:
        results = prompts

    # TODO: pass counts, all_count, active_filter too
    return render_template("index.html",
        prompts=results, query=query)
request.args vs request.formrequest.form reads POST body data (form submissions). request.args reads GET query parameters (from the URL). Search uses GET, so you use request.args. This is a key distinction.

Case-insensitive search — Both query and the field values are lowercased before comparison. This way "AERIAL", "Aerial", and "aerial" all match. Call .lower() on both sides.

Sketch the Connections

Before Day 32, draw this diagram (on paper or a notes app). Understanding the data flow before writing code makes implementation faster:

Browser Flask Route External Call Template
Generate form POST /generate Claude API index.html with generated=
Save Generated POST /save-generated save_prompts() redirect to /
Search form GET /search?q=aerial load_prompts() index.html with query=

End of Day Checklist

Tomorrow — Build: Claude Integration

Day 32 is a 75-minute build session. You'll add the /generate route to app.py, wire in the Anthropic SDK, add the generate form and result display to index.html, and add the /save-generated route. By the end, the Prompt Vault generates prompts with AI.